<!DOCTYPE html>

<center>
  <img id="img" width="100%"><br/>
  Stream <select id="select"><option id="from">from</option></select>
  add <input id="pass" placeholder="password" type="password">
  resolution <input id="res" value="240" step="32" max="1080" type="number" style="width:3em;">
  or <input id="upload" type="file">
</center>
<video id="video" width="100%" controls autoplay style="display: none;"></video>
<canvas id="canvas" width="0" style="display: none;"></canvas>

<script src="../jquery.js"></script>
<script src="../../../gun/gun.js"></script>
<script src="../../../gun/sea.js"></script>
<script src="../../../gun/lib/webrtc.js"></script>

<script>;(async function(){
gun = Gun(location.origin + '/gun'); //gun = GUN();

stream = canvas.getContext('2d'), stream.from = navigator.mediaDevices;

(await stream.from.enumerateDevices()).forEach((device,i) => {
  if('videoinput' !== device.kind){ return }
  var opt = $(from).clone().prependTo('select').get(0);
  $(opt).text(opt.id = device.label || 'Camera '+i);
  opt.value = device.deviceId;
});

$('select').on('change', async eve => { $(from).text('Off'); // update label
  if('Off' == select.value){ return video.srcObject.getTracks()[0].stop() }
  video.srcObject = await stream.from.getUserMedia({ audio: false,
    video: (select.value && {deviceId: {exact: select.value}}) || {facingMode: "environment"}
  });
});
$('#upload').on('change', async eve => { console.log("Check ./upload.html") })

setInterval(async tmp => {
  if(!(video.srcObject||'').active){ return }
  var size = parseInt(res.value);
  stream.drawImage(video, 0,0,
    canvas.width = size || video.videoWidth * 0.1,
    canvas.height = (size * (video.videoHeight/video.videoWidth)) || video.videoHeight * 0.1
  );
  var b64 = canvas.toDataURL('image/jpeg');
  if(pass.value){ b64 = await SEA.encrypt(b64, pass.value) }
  gun.get('test').get('video').put(b64);
}, 99);

gun.get('test').get('video').on(async data => {
  if(pass.value){ data = await SEA.decrypt(data, pass.value) }
  img.src = data; // Beware: Some browsers memory leak fast src updates.
});

// === AUDIO STREAMING WITH FADE-IN/OUT ===
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const mic = await stream.from.getUserMedia({ audio: true });
const src = audioCtx.createMediaStreamSource(mic);
const proc = audioCtx.createScriptProcessor(2048, 1, 1);

src.connect(proc);
proc.connect(audioCtx.destination);

proc.onaudioprocess = async e => {
  const input = e.inputBuffer.getChannelData(0);
  const output = new Float32Array(input.length);
  const fade = 128;

  for (let i = 0; i < fade; i++) {
    output[i] = input[i] * (i / fade); // fade in
  }
  for (let i = fade; i < input.length - fade; i++) {
    output[i] = input[i]; // middle
  }
  for (let i = input.length - fade; i < input.length; i++) {
    output[i] = input[i] * ((input.length - i) / fade); // fade out
  }

  const int16 = new Int16Array(output.length);
  for (let i = 0; i < output.length; i++) {
    int16[i] = Math.max(-32768, Math.min(32767, output[i] * 32768));
  }

  let b64 = btoa(String.fromCharCode(...new Uint8Array(int16.buffer)));
  if(pass.value){ b64 = await SEA.encrypt(b64, pass.value) }
  gun.get('test').get('audio').put(b64);
};

gun.get('test').get('audio').on(async data => {
  if(!data) return;
  if(pass.value){ data = await SEA.decrypt(data, pass.value) }
  const bin = atob(data);
  const bytes = new Uint8Array(bin.length);
  for(let i=0; i<bin.length; i++){ bytes[i] = bin.charCodeAt(i) }
  const buf = audioCtx.createBuffer(1, bytes.length / 2, 44100);
  const chan = buf.getChannelData(0);
  for(let i=0; i<chan.length; i++){
    const s = (bytes[i*2+1] << 8) | bytes[i*2];
    chan[i] = (s > 32767 ? s - 65536 : s) / 32768;
  }
  const player = audioCtx.createBufferSource();
  player.buffer = buf;
  player.connect(audioCtx.destination);
  player.start();
});

}());</script>
